﻿Imports System.Collections.Generic
Imports System.Collections

Public Class backtracking

    Private systems As List(Of InitializeData.SecondarySystem)
    Protected startTemperature As Double
    Protected stopTemperature As Double
    Public cycles As Integer
    Public cycl As Integer = 0
    Public Shared return_cost As Integer = 0
    Shared pick As Integer
    Public temperature As Double

    Public Shared return_temperature As Double

    Public BestSolution As New Dictionary(Of InitializeData.TVWS(), InitializeData.SecondarySystem)

    Public fragmentation As Double = 0.0

    Public Shared Spectrum_Utilization As Double = 0.0

    Public Shared bw_exploited As Double = 0.0

    Public Shared it As Integer = 0

    Public Shared return_fragmentation As Double = 0.0

    Public Shared tvws_local As New List(Of InitializeData.TVWS)

    Public Shared Total_channelsUsed As New List(Of InitializeData.TVWS)

    Public channelsUsed As New List(Of InitializeData.TVWS)

    Public Temp_channels_first As New List(Of InitializeData.TVWS)

    Public cost As Integer
    Public Shared fdd As Boolean = False
    Public Shared start_time As Double = 0.0
    Public Shared end_time As Double = 0.0
    Public ratio As Double


    Public Sub New(ByVal network As List(Of InitializeData.SecondarySystem))
        Me.systems = network
    End Sub

    Protected Function evaluate(ByVal solutionPerSystem As Dictionary(Of InitializeData.TVWS(), InitializeData.SecondarySystem)) As Double
        Dim Temp_Channels As New List(Of InitializeData.TVWS())
        Dim Last_Channels As New List(Of InitializeData.TVWS)
        Dim Temp_channel As InitializeData.TVWS = Nothing
        Dim success As Double = 0.1
        Dim fragmenta As Double = 0.0
        For Each entry As KeyValuePair(Of InitializeData.TVWS(), InitializeData.SecondarySystem) In solutionPerSystem
            Dim currentSystem As InitializeData.SecondarySystem = entry.Value
            Dim channels As InitializeData.TVWS() = entry.Key
            For Each currentChannel As InitializeData.TVWS In channels
                If (channelsUsed.Contains(currentChannel)) Then
                    channelsUsed.Clear()
                    Return 0.1
                End If
                If (Total_channelsUsed.Contains(currentChannel)) Then
                    channelsUsed.Clear()
                    Return 0.1
                End If
                If (currentChannel.transmissionPower < currentSystem.transmissionPower) Then
                    channelsUsed.Clear()
                    Return 0.1
                End If

                Temp_channel = currentChannel
                channelsUsed.Add(currentChannel)
            Next currentChannel
            Last_Channels.Add(Temp_channel)
            Temp_Channels.Add(channels)
        Next entry
        Dim Temp_channels_As_List As New List(Of InitializeData.TVWS)
        For k As Integer = 0 To Temp_Channels.Count - 1
            For p As Integer = 0 To Temp_Channels.Item(k).Length - 1
                Temp_channels_As_List.Add(Temp_Channels.Item(k)(p))
            Next p
        Next k
        For i As Integer = 0 To Temp_channels_As_List.Count - 1
            For j As Integer = 0 To Last_Channels.Count - 1
                If (Temp_channels_As_List.Item(i) Is Last_Channels.Item(j)) Then
                    Temp_channels_As_List.Remove(Last_Channels.Item(j))
                End If
            Next j
        Next i
        If (fdd) Then
            success = EvaluateFDD(solutionPerSystem)
            If (success = 0.1) Then
                Return 0.1
            End If
        End If

        fragmenta = check_fragmentation(Temp_channels_As_List)
        If (it = 0) Then
            Temp_channels_first = Temp_channels_As_List
            BestSolution = solutionPerSystem
            fragmentation = fragmenta
            return_fragmentation = fragmenta
            return_cost = cost
            return_temperature = temperature
            it = it + 1
            reversePreviousTVWS()
            Return 1.0
        End If
        If (fragmenta < fragmentation) Then
            BestSolution = solutionPerSystem
            return_cost = cost
            return_temperature = temperature
            return_fragmentation = fragmenta
            fragmentation = fragmenta
            reversePreviousTVWS()
        Else
            reverse_tvws(Temp_channels_As_List)
        End If
        Return 1.0
    End Function

    Public Function check_fragmentation(ByVal Temp_Channels As List(Of InitializeData.TVWS))
        Dim fragment As New List(Of Double)
        Dim total_fragment As Double = 0.0
        Dim f As Double = 0.0
        Dim total_fragment_square As Double = 0.0
        Dim total As Double = 0.0
        Dim Temp_Channels_used As New List(Of InitializeData.TVWS)
        Temp_Channels_used = Temp_Channels
        For i As Integer = 0 To Temp_Channels_used.Count - 1
            For j As Integer = 0 To tvws_local.Count - 1
                If ((Temp_Channels_used.Item(i).id = tvws_local.Item(j).id) And (Temp_Channels_used.Item(i).subid = tvws_local.Item(j).subid)) Then
                    tvws_local.Item(j).isAvailable = False
                End If
            Next j
        Next i
        For i As Integer = 0 To tvws_local.Count - 1
            If tvws_local.Item(i).isAvailable = True Then
                f = f + 1
            ElseIf tvws_local.Item(i).isAvailable = False Then
                fragment.Add(f)
                f = 0.0
            End If
            If i = tvws_local.Count - 1 Then
                fragment.Add(f)
                f = 0.0
            End If
        Next i
        For i As Integer = 0 To fragment.Count - 1
            If Not Double.IsNaN(fragment.Item(i)) Then
                total_fragment = total_fragment + fragment.Item(i)
            End If
        Next i
        For i As Integer = 0 To fragment.Count - 1
            If Not Double.IsNaN(fragment.Item(i)) Then
                total_fragment_square = total_fragment_square + (fragment.Item(i) * fragment.Item(i))
            End If
        Next i
        total = total_fragment_square / (total_fragment * total_fragment)
        Return 1 - total
    End Function



    Public Sub reversePreviousTVWS()
        For Each entry As KeyValuePair(Of InitializeData.TVWS(), InitializeData.SecondarySystem) In BestSolution
            Dim channels As InitializeData.TVWS() = entry.Key
            For Each currentChannel As InitializeData.TVWS In channels
                For i As Integer = 0 To tvws_local.Count - 1
                    If (tvws_local.Item(i) Is currentChannel) Then
                        tvws_local.Item(i).isAvailable = True
                    End If
                Next i
            Next currentChannel
        Next entry
    End Sub
    Public Sub reverse_tvws(ByVal Temp_Channels As List(Of InitializeData.TVWS))
        For i As Integer = 0 To Temp_Channels.Count - 1
            For j As Integer = 0 To tvws_local.Count - 1
                If ((Temp_Channels.Item(i).id = tvws_local.Item(j).id) And (Temp_Channels.Item(i).subid = tvws_local.Item(j).subid)) Then
                    tvws_local.Item(j).isAvailable = True
                End If
            Next j
        Next i
    End Sub


    Public Function EvaluateFDD(ByVal solutionPerSystem As Dictionary(Of InitializeData.TVWS(), InitializeData.SecondarySystem)) As Double
        Dim success As New List(Of Double)
        Dim ID As New List(Of Integer)
        Dim Channels_Temp As New List(Of InitializeData.TVWS())
        Dim First_Channel_ID As Integer = 0
        Dim First_Channel_subID As Integer = 0
        Dim Second_Channel_ID As Integer = 0
        Dim Second_Channel_subID As Integer = 0
        For Each entry As KeyValuePair(Of InitializeData.TVWS(), InitializeData.SecondarySystem) In solutionPerSystem
            Dim channels As InitializeData.TVWS() = entry.Key
            Dim system As InitializeData.SecondarySystem = entry.Value
            If (system.name Is "LTE DL") Then
                ID.Add(system.id)
                Channels_Temp.Add(channels)
            End If
            If (system.name Is "LTE UL") Then
                ID.Add(system.id)
                Channels_Temp.Add(channels)
            End If
        Next entry
        removeDuplicate(ID)
        For i As Integer = 0 To ID.Count - 1
            For Each entry As KeyValuePair(Of InitializeData.TVWS(), InitializeData.SecondarySystem) In solutionPerSystem
                Dim Channels As InitializeData.TVWS() = entry.Key
                Dim system As InitializeData.SecondarySystem = entry.Value
                If (system.name Is "LTE DL" And system.id = ID.Item(i)) Then
                    For Each currentChannel As InitializeData.TVWS In Channels
                        First_Channel_ID = currentChannel.id
                        First_Channel_subID = currentChannel.subid
                        Exit For
                    Next currentChannel
                End If
                If (system.name Is "LTE UL" And system.id = ID.Item(i)) Then
                    For Each currentChannel As InitializeData.TVWS In Channels
                        Second_Channel_ID = currentChannel.id
                        Second_Channel_subID = currentChannel.subid
                        Exit For
                    Next currentChannel
                End If

            Next entry
            Dim min As Integer = Math.Min(First_Channel_ID, Second_Channel_ID)
            If (min = First_Channel_ID) Then
                Dim sum_first As Integer = (Second_Channel_ID * 8) + Second_Channel_subID
                Dim sum_Second As Integer = (First_Channel_ID * 8) + First_Channel_subID
                If ((sum_first - sum_Second) < 30 Or (sum_first - sum_Second) > 48) Then
                    Return 0.1
                End If
            ElseIf (min = Second_Channel_ID) Then
                If (((((First_Channel_ID * 8) + First_Channel_subID) - ((Second_Channel_ID * 8) + Second_Channel_subID)) < 35) Or ((((First_Channel_ID * 8) + First_Channel_subID) - ((Second_Channel_ID * 8) + Second_Channel_subID)) > 45)) Then
                    Return 0.1
                End If
            End If

        Next i
        Return 1.0
    End Function

    Protected Function randomize(ByVal systems As List(Of InitializeData.SecondarySystem)) As Dictionary(Of InitializeData.TVWS(), InitializeData.SecondarySystem)
        Dim solutionPerSystem As New Dictionary(Of InitializeData.TVWS(), InitializeData.SecondarySystem)
        Dim random As New Random(Now.Millisecond)
        For index As Integer = 0 To systems.Count - 1
            Dim systemk As InitializeData.SecondarySystem = systems.Item(index)
            pick = (random.Next - temperature) Mod systemk.potentialSolutions.Count

            Dim solution As InitializeData.TVWS() = systemk.potentialSolutions.Item(pick)
            solutionPerSystem.Add(solution, systemk)
        Next index
        Return solutionPerSystem
    End Function

    Public Sub start(ByVal startTemp As Double, ByVal stopTemp As Double, ByVal cycles As Integer)

        Me.temperature = startTemp
        Me.cycles = cycles
        Me.startTemperature = startTemp
        Me.stopTemperature = stopTemp
        tvws_local = Main.tvws
        it = 0
    End Sub



    Public Shared Sub removeDuplicate(ByRef arlList As List(Of Integer))
        Dim h As New HashSet(Of Integer)
        For Each item As Integer In arlList
            h.Add(item)
        Next item
        arlList.Clear()
        For Each item As Integer In h
            arlList.Add(item)
        Next item
    End Sub

    Public Function calculateStartFragmentation() As Double
        Dim frag As Double = 0.0
        Dim frag_square As Double = 0.0
        Dim f As Double = 0.0
        Dim total As Double = 0.0
        Dim fragment As New List(Of Double)
        Dim first As Boolean = True
        For i As Integer = 0 To Main.tvws.Count - 1
            Dim space As InitializeData.TVWS = Main.tvws.Item(i)
            If (space.isAvailable = True) Then
                f = f + space.bandwidth
                first = True
            ElseIf (space.isAvailable = False) Then
                If (first) Then
                    fragment.Add(f)
                    f = 0.0
                    first = False
                End If
                fragment.Add(f)
                f = 0.0
            End If
        Next i
        For i As Integer = 0 To fragment.Count - 1
            If Not Double.IsNaN(fragment.Item(i)) Then
                frag = frag + fragment.Item(i)
            End If
        Next i
        For i As Integer = 0 To fragment.Count - 1
            If Not Double.IsNaN(fragment.Item(i)) Then
                frag_square = frag_square + (fragment.Item(i) * fragment.Item(i))
            End If
        Next i
        total = frag_square / (frag * frag)
        Return 1 - total
    End Function
    Public Sub Initialization()
        Me.temperature = Me.startTemperature
        it = 0
        Me.BestSolution.Clear()
        backtracking.return_cost = 0
        backtracking.return_temperature = 0
        Me.channelsUsed.Clear()
        tvws_local = Main.tvws
        Me.cost = 0
    End Sub
    Public Function back(ByVal systems As List(Of InitializeData.SecondarySystem)) As Dictionary(Of InitializeData.TVWS(), InitializeData.SecondarySystem)
        Dim solutionPerSystem As New Dictionary(Of InitializeData.TVWS(), InitializeData.SecondarySystem)
        ratio = Math.Exp(Math.Log(stopTemperature / startTemperature)) / (cycles - 1)
        Dim success As Double = 0.1
        start_time = TimeOfDay.Millisecond
        While (temperature > stopTemperature)
            For Me.cycl = 0 To Me.cycles
                solutionPerSystem = randomize(systems)
                success = evaluate(solutionPerSystem)
                cost = cost + 1
            Next cycl
            temperature = temperature * ratio
        End While
        Dim LastChannels As New List(Of InitializeData.TVWS)
        Dim tv As InitializeData.TVWS = Nothing
        For Each Entry As KeyValuePair(Of InitializeData.TVWS(), InitializeData.SecondarySystem) In BestSolution
            Dim channels As InitializeData.TVWS() = Entry.Key
            For Each currentChannel As InitializeData.TVWS In channels
                tv = currentChannel
            Next currentChannel
            LastChannels.Add(tv)
        Next Entry
        For Each Entry As KeyValuePair(Of InitializeData.TVWS(), InitializeData.SecondarySystem) In BestSolution
            Dim channels As InitializeData.TVWS() = Entry.Key
            For Each currentChannel As InitializeData.TVWS In channels
                For i As Integer = 0 To tvws_local.Count - 1
                    If (LastChannels.Contains(currentChannel)) Then
                        If (tvws_local.Item(i) Is currentChannel) Then
                            tvws_local.Item(i).isAvailable = True
                        End If
                    Else
                        If (tvws_local.Item(i) Is currentChannel) Then
                            tvws_local.Item(i).isAvailable = False
                        End If

                    End If

                Next i
            Next currentChannel
        Next Entry
        For Each entry As KeyValuePair(Of InitializeData.TVWS(), InitializeData.SecondarySystem) In BestSolution
            Dim channels As InitializeData.TVWS() = entry.Key
            For Each channel As InitializeData.TVWS In channels
                Total_channelsUsed.Add(channel)
            Next channel
        Next entry
        Spectrum_Utilization = CalculateSpecrtumUtilization()
        bw_exploited = CalculateBWExploited()
        Main.tvws = tvws_local
        end_time = TimeOfDay.Millisecond
        Return BestSolution
    End Function

    Public Function CalculateSpecrtumUtilization() As Double
        Dim f As Double = 0.0
        For i As Integer = 0 To tvws_local.Count - 1
            If (tvws_local.Item(i).isAvailable = False) Then
                f = f + 1
            End If
        Next i
        Return f / 168
    End Function




    Public Function CalculateBWExploited() As Double
        Dim f As Double = 0.0
        For i As Integer = 0 To tvws_local.Count - 1
            If (tvws_local.Item(i).isAvailable = False) Then
                f = f + 1
            End If
        Next i
        Return f
    End Function
End Class
